package com.cdtu.store.search.service;


import com.cdtu.store.common.entity.*;
import com.cdtu.store.search.client.BrandClient;
import com.cdtu.store.search.client.CategoryClient;
import com.cdtu.store.search.client.GoodsClient;
import com.cdtu.store.search.client.SpecificationClient;
import com.cdtu.store.search.entity.Goods;
import com.cdtu.store.search.entity.SearchRequest;
import com.cdtu.store.search.entity.SearchResult;
import com.cdtu.store.search.repository.GoodsRepository;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author dongyuhao
 */
@Service
public class SearchService {
    @Autowired
    private BrandClient brandClient;
    @Autowired
    private CategoryClient categoryClient;
    @Autowired
    private GoodsClient goodsClient;
    @Autowired
    private SpecificationClient specificationClient;
    @Autowired
    private GoodsRepository goodsRepository;
    @Autowired
    private ElasticsearchRestTemplate template;
    public static final ObjectMapper MAPPER = new ObjectMapper();
    public Goods buildGoods(SpuEntity spuEntity) throws IOException, ParseException {
        Goods goods = new Goods();
        //查询品牌
        BrandEntity brand = this.brandClient.queryBrandById(spuEntity.getBrandId());
        //查询分类名称
        List<String> names = this.categoryClient.queryNamesByIds(Arrays.asList(spuEntity.getCid1(), spuEntity.getCid2(), spuEntity.getCid3()));
        //查询spu下的所有sku
        List<SkuEntity> skus = this.goodsClient.querySkusBySpuId(spuEntity.getId());
        List<Long> prices = new ArrayList<>();
        List<Map<String, Object>> skuMapList = new ArrayList<>();
        skus.forEach(sku -> {
            prices.add(sku.getPrice());
            Map<String, Object> skuMap = new HashMap<>();
            skuMap.put("id", sku.getId());
            skuMap.put("title", sku.getTitle());
            skuMap.put("price", sku.getPrice());
            skuMap.put("image", StringUtils.isNotBlank(sku.getImages()) ? StringUtils.split(sku.getImages(), ",")[0] : "");
            skuMapList.add(skuMap);
        });
        //查询出所有的搜索规格参数
        List<SpecParamEntity> specParamEntities = this.specificationClient.queryParams(null, spuEntity.getCid3(), null, 1);
        //查询spuDetail,获取规格参数值
        SpuDetailEntity spuDetailEntity = this.goodsClient.querySpuDetailById(spuEntity.getId());
        // 获取通用的规格参数
        Map<Long, Object> genericSpecMap = MAPPER.readValue(spuDetailEntity.getGenericSpec(), new TypeReference<Map<Long, Object>>() {
        });
        // 获取特殊的规格参数
        Map<Long, List<Object>> specialSpecMap = MAPPER.readValue(spuDetailEntity.getSpecialSpec(), new TypeReference<Map<Long, List<Object>>>() {
        });

        Map<String, Object> paramMap = new HashMap<>();
        specParamEntities.forEach(param -> {
            // 判断是否通用规格参数
            if (param.getGeneric() == 1) {
                // 获取通用规格参数值
                String value = genericSpecMap.get(param.getId()).toString();
                // 判断是否是数值类型
                if (param.getWhetherNumeric() == 1) {
                    // 如果是数值的话，判断该数值落在那个区间
                    value = chooseSegment(value, param);
                }
                // 把参数名和值放入结果集中
                paramMap.put(param.getName(), value);
            } else {
                paramMap.put(param.getName(), specialSpecMap.get(param.getId()));
            }
        });
        // 设置参数
        goods.setId(spuEntity.getId());
        goods.setCid1(spuEntity.getCid1());
        goods.setCid2(spuEntity.getCid2());
        goods.setCid3(spuEntity.getCid3());
        goods.setBrandId(spuEntity.getBrandId());
        goods.setCreateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(spuEntity.getCreateTime()));
        goods.setSubTitle(spuEntity.getSubTitle());
        goods.setAll(spuEntity.getTitle() + brand.getName() + StringUtils.join(names, " "));
        goods.setPrice(prices);
        goods.setSkus(MAPPER.writeValueAsString(skuMapList));
        goods.setSpecs(paramMap);
        return goods;
    }

    private String chooseSegment(String value, SpecParamEntity p) {
        double val = NumberUtils.toDouble(value);
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = NumberUtils.toDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if(segs.length == 2){
                end = NumberUtils.toDouble(segs[1]);
            }
            // 判断是否在范围内
            if(val >= begin && val < end){
                if(segs.length == 1){
                    result = segs[0] + p.getUnit() + "以上";
                }else if(begin == 0){
                    result = segs[1] + p.getUnit() + "以下";
                }else{
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    //public PageResult<Goods> search(SearchRequest request) {
    //    String key = request.getKey();
    //    // 判断是否有搜索条件，如果没有，直接返回null。不允许搜索全部商品
    //    if (StringUtils.isBlank(key)) {
    //        return null;
    //    }
    //
    //    // 构建查询条件
    //    NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
    //
    //    // 1、对key进行全文检索查询
    //    queryBuilder.withQuery(QueryBuilders.matchQuery("all", key).operator(Operator.AND));
    //
    //    // 2、通过sourceFilter设置返回的结果字段,我们只需要id、skus、subTitle
    //    queryBuilder.withSourceFilter(new FetchSourceFilter(
    //            new String[]{"id","skus","subTitle"}, null));
    //
    //    // 3、分页
    //    // 准备分页参数
    //    int page = request.getPage();
    //    int size = request.getSize();
    //    queryBuilder.withPageable(PageRequest.of(page - 1, size)).build();
    //    String sortBy = request.getSortBy();
    //    boolean desc = request.getDescending();
    //    System.out.println(desc);
    //    if(StringUtils.isNotBlank(sortBy)){
    //        queryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(desc ? SortOrder.DESC : SortOrder.ASC));
    //    }
    //    // 4、查询，获取结果
    //    final Pageable pageable = queryBuilder.build().getPageable();
    //    //SearchHits<Goods> search = this.template.search(queryBuilder.build(),Goods.class);
    //    //SearchPage<Goods> searchHits = SearchHitSupport.searchPageFor(search, pageable);
    //    Page<Goods> goodsPage = this.goodsRepository.search(queryBuilder.build());
    //    // 封装结果并返回
    //    return new PageResult<Goods>(goodsPage.getTotalElements(), goodsPage.getTotalPages(), goodsPage.getContent());
    //}

    public PageResult<SearchHit<Goods>> search(SearchRequest request) {
        String key = request.getKey();
        if (StringUtils.isBlank(key)) {
            return null;
        }
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //MatchQueryBuilder basicQuery = QueryBuilders.matchQuery("all", key).operator(Operator.AND);
        BoolQueryBuilder boolQueryBuilder = buildBooleanQueryBuilder(request);
        queryBuilder.withQuery(boolQueryBuilder);
        //全文检索key
        //queryBuilder.withQuery(basicQuery);
        //过滤字段,参数一:想要的字段,参数二:不想要的字段
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"},null));
        //排序
        String sortBy = request.getSortBy();
        boolean descending = request.getDescending();
        if(StringUtils.isNotBlank(sortBy)){
            queryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(descending ? SortOrder.DESC:SortOrder.ASC));
        }
        int page = request.getPage();
        int size = request.getSize();
        //分页查询
        Pageable pageable = queryBuilder.withPageable(PageRequest.of(page - 1, size)).build().getPageable();

        //聚合查询
        String categoryAggName = "categories";
        String brandAggName = "brands";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));

        SearchHits<Goods> search = template.search(queryBuilder.build(), Goods.class);
        SearchPage<Goods> searchHits = SearchHitSupport.searchPageFor(search, pageable);
        Aggregations aggregations = search.getAggregations();
        assert aggregations != null;
        //解析聚合集
        List<Map<String, Object>> categorys = getCategoryAggResult(aggregations.get(categoryAggName));
        List<BrandEntity> brands = getBrandAggResult(aggregations.get(brandAggName));
        List<Map<String,Object>> specs = null;
        if(categorys.size() == 1){
            specs = getParamAggResult((Long)categorys.get(0).get("id"), boolQueryBuilder);
        }
        //long totalHits = search.getTotalHits();
        //System.out.println(totalHits);
        //search.forEach(searchHit -> System.out.println(searchHit.getContent()));
        return new SearchResult<SearchHit<Goods>>(searchHits.getTotalElements(),searchHits.getTotalPages(),search.getSearchHits(),categorys,brands,specs);
    }
    /**
     * 解析品牌聚合结果集
     * @param parsedLongTerms
     * @return
     */
    private List<BrandEntity> getBrandAggResult(ParsedLongTerms parsedLongTerms) {
        // 定义一个品牌集合，搜集所有的品牌对象
        List<BrandEntity> brands = new ArrayList<>();
        // 解析所有的id桶，查询品牌
        parsedLongTerms.getBuckets().forEach(bucket -> {
            BrandEntity brand = this.brandClient.queryBrandById(bucket.getKeyAsNumber().longValue());
            brands.add(brand);
        });
        return brands;
        // 解析聚合结果集中的桶，把桶的集合转化成id的集合
        // List<Long> brandIds = terms.getBuckets().stream().map(bucket -> bucket.getKeyAsNumber().longValue()).collect(Collectors.toList());
        // 根据ids查询品牌
    }

    private List<Map<String,Object>> getCategoryAggResult(ParsedLongTerms parsedLongTerms) {
        // 获取所有的分类id桶
        // 定义一个品牌集合，搜集所有的品牌对象
        List<Map<String, Object>> categories = new ArrayList<>();
        List<Long> cids = new ArrayList<>();
        // 解析所有的id桶，查询品牌
        parsedLongTerms.getBuckets().forEach(bucket -> {
            cids.add(bucket.getKeyAsNumber().longValue());
        });
        List<String> names = this.categoryClient.queryNamesByIds(cids);
        for (int i = 0; i < cids.size(); i++) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", cids.get(i));
            map.put("name", names.get(i));
            categories.add(map);
        }
        return categories;
    }
    /**
     * 聚合出规格参数过滤条件
     * @param id
     * @param basicQuery
     * @return
     */
    public List<Map<String,Object>> getParamAggResult(Long id, QueryBuilder basicQuery){
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(basicQuery);

        List<SpecParamEntity> params = this.specificationClient.queryParams(null, id, null, 1);
        params.forEach(param ->{
            queryBuilder.addAggregation(AggregationBuilders.terms(param.getName()).field("specs." + param.getName() + ".keyword"));
        });
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{}, null));
        //执行聚合查询
        SearchHits<Goods> search = template.search(queryBuilder.build(), Goods.class);
        // 定义一个集合，收集聚合结果集
        List<Map<String, Object>> paramMapList = new ArrayList<>();
        Map<String, Aggregation> aggregationMap = search.getAggregations().asMap();
        for (Map.Entry<String, Aggregation> entry : aggregationMap.entrySet()){
            Map<String, Object> map = new HashMap<>();
            // 放入规格参数名
            map.put("k", entry.getKey());
            // 收集规格参数值
            List<Object> options = new ArrayList<>();
            // 解析每个聚合
            ParsedStringTerms terms = (ParsedStringTerms) entry.getValue();
            // 遍历每个聚合中桶，把桶中key放入收集规格参数的集合中
            terms.getBuckets().forEach(bucket -> options.add(bucket.getKeyAsString()));
            map.put("options", options);
            paramMapList.add(map);
        }
        return paramMapList;
    }
    /**
     * 构建bool查询构建器
     * @param request
     * @return
     */
    private BoolQueryBuilder buildBooleanQueryBuilder(SearchRequest request) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 添加基本查询条件
        boolQueryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND));

        // 添加过滤条件
        if (CollectionUtils.isEmpty(request.getFilter())){
            return boolQueryBuilder;
        }
        for (Map.Entry<String, String> entry : request.getFilter().entrySet()) {

            String key = entry.getKey();
            // 如果过滤条件是“品牌”, 过滤的字段名：brandId
            if (StringUtils.equals("品牌", key)) {
                key = "brandId";
            } else if (StringUtils.equals("分类", key)) {
                // 如果是“分类”，过滤字段名：cid3
                key = "cid3";
            } else {
                // 如果是规格参数名，过滤字段名：specs.key.keyword
                key = "specs." + key + ".keyword";
            }
            boolQueryBuilder.filter(QueryBuilders.termQuery(key, entry.getValue()));
        }

        return boolQueryBuilder;
    }

    public void deleteIndex(Long id){
        this.goodsRepository.deleteById(id);
    }

    public void createIndex(Long id) throws IOException, ParseException {
        SpuEntity spu = this.goodsClient.querySpuById(id);
        // 构建商品
        Goods goods = this.buildGoods(spu);

        // 保存数据到索引库
        this.goodsRepository.save(goods);
    }
}
